/*
 * Copyright (c) 2018-2999 湖南洛云网络科技有限公司 All rights reserved.
 *
 * https://www.gz-yami.com/
 *
 */
package com.yami.shop.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.yami.shop.bean.enums.FlowLogPageEnum;
import com.yami.shop.bean.enums.FlowVisitEnum;
import com.yami.shop.bean.model.FlowLog;
import com.yami.shop.bean.param.ProdAnalysisSurveyParam;
import com.yami.shop.common.config.Constant;
import com.yami.shop.common.util.CacheManagerUtil;
import com.yami.shop.dao.FlowLogMapper;
import com.yami.shop.service.FlowLogService;
import lombok.AllArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户流量记录
 *
 * @author YXF
 * @date 2020-07-13 13:18:33
 */
@Service
@AllArgsConstructor
public class FlowLogServiceImpl extends ServiceImpl<FlowLogMapper, FlowLog> implements FlowLogService {

    private final FlowLogMapper flowLogMapper;

    private final CacheManagerUtil cacheManagerUtil;

    private final RedisTemplate redisTemplate;

    private final String CACHE_NAME = "FlowAnalysis";
    private final String CACHE_KEY = "flowLogList";

    @Override
    public Date getMinCreateTime() {
        return flowLogMapper.getMinCreateTime();
    }

    @Override
    public void insertBatch() {
        //redis 中的数据数量
        Long logNums = redisTemplate.opsForSet().size(Constant.FLOW_ANALYSIS_LOG);
        Object flowAnalysisLogObject = redisTemplate.opsForSet().pop(Constant.FLOW_ANALYSIS_LOG,logNums);
        List<FlowLog> flowLogList = (List<FlowLog>) flowAnalysisLogObject;
        //按照uuidSession进行分组,分别统计每个用户的操作行为
        Map<String, List<FlowLog>> flowLogAsUidSession = flowLogList.stream().collect(Collectors.groupingBy(FlowLog::getUuidSession));
        List<FlowLog> newFlowLogList = Lists.newArrayList();
        for (String uuidSession:flowLogAsUidSession.keySet()){
            //按照用户操作的步数进行分组（相同步数必定属于同一个页面）
            Map<Integer, List<FlowLog>> flowLogAsStep = flowLogAsUidSession.get(uuidSession).stream().collect(Collectors.groupingBy(FlowLog::getStep));
            for (Integer step:flowLogAsStep.keySet()){
                List<FlowLog> flowLogListAsClick = Lists.newArrayList();
                List<FlowLog> flowLogListAsShopCat = Lists.newArrayList();
                for (FlowLog flowLog:flowLogAsStep.get(step)){
                    // 分享页面、访问页面
                    if (FlowVisitEnum.isVisitOrShare(flowLog.getVisitType())){
                        //页面访问直接添加到集合
                        flowLog.setNums(1);
                        newFlowLogList.add(flowLog);
                    }
                    // 点击页面
                    else if (FlowVisitEnum.CLICK.value().equals(flowLog.getVisitType())){
                        //页面点击操作添加到新的集合，统计操作的数量（多条相同的操作数据统计为一条），减少插入的数据数量
                        flowLogListAsClick.add(flowLog);
                    }
                    // 加购
                    else {
                        //同上
                        flowLogListAsShopCat.add(flowLog);
                    }
                }
                if (flowLogListAsClick.size() > 0){
                    //对页面点击数据处理，统计操作的数量（多条相同的操作数据统计为一条）
                    newFlowLogList.addAll(getClickOrShopCatFlowLog(flowLogListAsClick, Boolean.FALSE));
                }
                if (flowLogListAsShopCat.size() > 0){
                    //对加购数据处理，统计操作的数量（多条相同的操作数据统计为一条）
                    newFlowLogList.addAll(getClickOrShopCatFlowLog(flowLogListAsShopCat, Boolean.TRUE));
                }
            }
        }
        // 数据保存到数据库中
        saveBatch(newFlowLogList);
        //当天的数据写入缓存中，用于实时数据的统计
        List<FlowLog> flowLogListCurrent = Lists.newArrayList();
        Long currentTime = DateUtil.beginOfDay(new Date()).getTime();
        for (FlowLog flowLog:newFlowLogList){
            if (flowLog.getCreateTime().getTime() >= currentTime){
                flowLog.setHour(DateUtil.hour(flowLog.getCreateTime(), true));
                flowLogListCurrent.add(flowLog);
            }
        }
        List<FlowLog> flowLogListCache = cacheManagerUtil.getCache(CACHE_NAME, CACHE_KEY);
        if (Objects.nonNull(flowLogListCache) && !Objects.equals(flowLogListCache.size(),0)){
            FlowLog flowLog = flowLogListCache.get(flowLogListCache.size()-1);
            //当天开始时间大于记录时间（代表新的一天），更新数据
            if (currentTime > flowLog.getCreateTime().getTime()){
                flowLogListCache =  flowLogListCurrent;
            }else {
                flowLogListCache.addAll(flowLogListCurrent);
            }
        }else {
            flowLogListCache =  flowLogListCurrent;
        }
        flowLogListCache.sort(Comparator.comparing(FlowLog::getCreateTime));
        flowLogPutCache(flowLogListCache);
    }

    /**
     *  获取用户点击或加购的数量
     * @param flowLogList 源数据
     * @param isShopCat 是否是加购操作
     * @return 统计后的用户操作记录
     */
    private List<FlowLog> getClickOrShopCatFlowLog(List<FlowLog> flowLogList, Boolean isShopCat){
        List<FlowLog> newFlowLogList = Lists.newArrayList();
        FlowLog flowLog = flowLogList.get(0);

        if (!FlowLogPageEnum.isProdInfo(flowLog.getPageId())){
            //非商品详情页
            flowLog.setNums(flowLogList.size());
            newFlowLogList.add(flowLog);
        }else {
            //商品详情页
            //根据BizData（prodId）进行分组
            Map<String, List<FlowLog>> flowLogListAsBizData = flowLogList.stream().filter(f-> StrUtil.isNotBlank(f.getBizData())).collect(Collectors.groupingBy(FlowLog::getBizData));
            List<FlowLog> flowLogListAsProdId = null;
            FlowLog flowLogAsProdId = null;
            //统计该商品相同行为的所有操作数据量
            for (String prodId:flowLogListAsBizData.keySet()){
                flowLogListAsProdId = flowLogListAsBizData.get(prodId);
                flowLogAsProdId = flowLogListAsProdId.get(0);
                if (isShopCat) {
                    flowLogAsProdId.setNums(flowLogListAsProdId.stream().mapToInt(FlowLog::getNums).sum());
                } else {
                    flowLogAsProdId.setNums(flowLogListAsProdId.size());
                }
                newFlowLogList.add(flowLogAsProdId);
            }
        }
        return newFlowLogList;
    }
    @Override
    public List<FlowLog> getFlowLogListByCache(){
        Date date = DateUtil.beginOfDay(new Date());
        //获取缓存
        insertBatch();
        List<FlowLog> flowLogList = cacheManagerUtil.getCache(CACHE_NAME, CACHE_KEY);
        Integer count  = flowLogMapper.getFlowLogCountByDate(date);
        if (count > flowLogList.size()){
            flowLogList = flowLogMapper.getFlowLogByDate(date,null);
            for (FlowLog flowLog :flowLogList){
                flowLog.setHour(DateUtil.hour(flowLog.getCreateTime(),true));
            }
            flowLogPutCache(flowLogList);
        }
        return flowLogList;
    }

    @Override
    public void flowLogPutCache(List<FlowLog> flowLogList){
        cacheManagerUtil.putCache(CACHE_NAME, CACHE_KEY, flowLogList);
    }

    @Override
    public List<FlowLog> getFlowLogByDate(Date startTime, Date endTime) {
        return flowLogMapper.getFlowLogByDate(startTime,endTime);
    }

    @Override
    public void deleteAllByDate(Date startTime, Date endTime) {
        flowLogMapper.deleteAllByDate(startTime,endTime);
    }

    @Override
    public Integer countVisitProdNum(ProdAnalysisSurveyParam param) {
        return flowLogMapper.countVisitProdNum(param);
    }

    @Override
    public Integer countVisitProdInfoNum(ProdAnalysisSurveyParam param) {
        return flowLogMapper.countVisitProdInfoNum(param);
    }

    @Override
    public Integer countProdExposeNum(ProdAnalysisSurveyParam param) {
        return flowLogMapper.countProdExposeNum(param);
    }

    @Override
    public Integer countProdVisitorNum(ProdAnalysisSurveyParam param) {
        return flowLogMapper.countProdVisitorNum(param);
    }

    @Override
    public Integer countAddCartProdNum(ProdAnalysisSurveyParam param) {
        Integer num = flowLogMapper.countAddCartProdNum(param);
        if (Objects.isNull(num)) {
            return 0;
        }
        return num;
    }

    @Override
    public Integer countShareVisit(ProdAnalysisSurveyParam param) {
        return flowLogMapper.countShareVisit(param);
    }


}
